/*
 * Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/>
 * Copyright (C) 2010-2015 Rising Gods <http://www.rising-gods.de/>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or (at your
 * option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program. If not, see <http://www.gnu.org/licenses/>.
 */

#include "ObjectMgr.h"
#include "ScriptMgr.h"
#include "ObjectDefines.h"
#include "GridNotifiers.h"
#include "GridNotifiersImpl.h"
#include "InstanceScript.h"
#include "ScriptedCreature.h"
#include "blackrock_spire.h"

uint32 const DragonspireMobs[3] = { NPC_BLACKHAND_DREADWEAVER, NPC_BLACKHAND_SUMMONER, NPC_BLACKHAND_VETERAN };

enum EventIds
{
    EVENT_DARGONSPIRE_ROOM_STORE           = 1,
    EVENT_DARGONSPIRE_ROOM_CHECK           = 2,
    EVENT_UROK_DOOMHOWL_SPAWNS_1           = 3,
    EVENT_UROK_DOOMHOWL_SPAWNS_2           = 4,
    EVENT_UROK_DOOMHOWL_SPAWNS_3           = 5,
    EVENT_UROK_DOOMHOWL_SPAWNS_4           = 6,
    EVENT_UROK_DOOMHOWL_SPAWNS_5           = 7,
    EVENT_UROK_DOOMHOWL_SPAWN_IN           = 8
};

class instance_blackrock_spire : public InstanceMapScript
{
    public:
        instance_blackrock_spire() : InstanceMapScript(BRSScriptName, 229) { }
    
        struct instance_blackrock_spireMapScript : public InstanceScript
        {
            instance_blackrock_spireMapScript(InstanceMap* map) : InstanceScript(map)
            {
                SetHeaders(DataHeader);
                SetBossNumber(EncounterCount);
                HighlordOmokk            .Clear();
                ShadowHunterVoshgajin    .Clear();
                WarMasterVoone           .Clear();
                MotherSmolderweb         .Clear();
                UrokDoomhowl             .Clear();
                QuartermasterZigris      .Clear();
                GizrultheSlavener        .Clear();
                Halycon                  .Clear();
                OverlordWyrmthalak       .Clear();
                PyroguardEmberseer       .Clear();
                WarchiefRendBlackhand    .Clear();
                Gyth                     .Clear();
                LordVictorNefarius       .Clear();
                TheBeast                 .Clear();
                GeneralDrakkisath        .Clear();
                go_emberseerin           .Clear();
                go_doors                 .Clear();
                go_emberseerout          .Clear();
                go_blackrockaltar        .Clear();


                go_portcullis_active     .Clear();
                go_portcullis_tobossrooms.Clear();
                go_drakkisath_door_one   .Clear();
                go_drakkisath_door_two   .Clear();
            }
    
            void OnCreatureCreate(Creature* creature) override
            {
                switch (creature->GetEntry())
                {
                    case NPC_HIGHLORD_OMOKK:
                        HighlordOmokk = creature->GetGUID();
                        break;
                    case NPC_SHADOW_HUNTER_VOSHGAJIN:
                        ShadowHunterVoshgajin = creature->GetGUID();
                        break;
                    case NPC_WARMASTER_VOONE:
                        WarMasterVoone = creature->GetGUID();
                        break;
                    case NPC_MOTHER_SMOLDERWEB:
                        MotherSmolderweb = creature->GetGUID();
                        break;
                    case NPC_UROK_DOOMHOWL:
                        UrokDoomhowl = creature->GetGUID();
                        break;
                    case NPC_QUARTERMASTER_ZIGRIS:
                        QuartermasterZigris = creature->GetGUID();
                        break;
                    case NPC_GIZRUL_THE_SLAVENER:
                        GizrultheSlavener = creature->GetGUID();
                        break;
                    case NPC_HALYCON:
                        Halycon = creature->GetGUID();
                        break;
                    case NPC_OVERLORD_WYRMTHALAK:
                        OverlordWyrmthalak = creature->GetGUID();
                        break;
                    case NPC_PYROGAURD_EMBERSEER:
                        PyroguardEmberseer = creature->GetGUID();
                        if (GetBossState(DATA_PYROGAURD_EMBERSEER) == DONE)
                            creature->DisappearAndDie();
                        break;
                    case NPC_WARCHIEF_REND_BLACKHAND:
                        WarchiefRendBlackhand = creature->GetGUID();
                        if (GetBossState(DATA_GYTH) == DONE)
                            creature->DisappearAndDie();
                        break;
                    case NPC_GYTH:
                        Gyth = creature->GetGUID();
                        break;
                    case NPC_THE_BEAST:
                        TheBeast = creature->GetGUID();
                        break;
                    case NPC_GENERAL_DRAKKISATH:
                        GeneralDrakkisath = creature->GetGUID();
                        break;
                    case NPC_LORD_VICTOR_NEFARIUS:
                        LordVictorNefarius = creature->GetGUID();
                        if (GetBossState(DATA_GYTH) == DONE)
                            creature->DisappearAndDie();
                        break;
                 }
             }
    
            void OnGameObjectCreate(GameObject* go) override
            {
                switch (go->GetEntry())
                {
                    case GO_WHELP_SPAWNER:
                        go->CastSpell(NULL, SPELL_SUMMON_ROOKERY_WHELP);
                        break;
                    case GO_EMBERSEER_IN:
                        go_emberseerin = go->GetGUID();
                        if (GetBossState(DATA_DRAGONSPIRE_ROOM) == DONE)
                            HandleGameObject(ObjectGuid::Empty, true, go);
                        break;
                    case GO_DOORS:
                        go_doors = go->GetGUID();
                        if (GetBossState(DATA_DRAGONSPIRE_ROOM) == DONE)
                            HandleGameObject(ObjectGuid::Empty, true, go);
                        break;
                    case GO_EMBERSEER_OUT:
                        go_emberseerout = go->GetGUID();
                        if (GetBossState(DATA_PYROGAURD_EMBERSEER) == DONE)
                            HandleGameObject(ObjectGuid::Empty, true, go);
                        break;
                    case GO_HALL_RUNE_1:
                        go_roomrunes[0] = go->GetGUID();
                        if (GetBossState(DATA_HALL_RUNE_1) == DONE)
                            HandleGameObject(ObjectGuid::Empty, false, go);
                        break;
                    case GO_HALL_RUNE_2:
                        go_roomrunes[1] = go->GetGUID();
                        if (GetBossState(DATA_HALL_RUNE_2) == DONE)
                            HandleGameObject(ObjectGuid::Empty, false, go);
                        break;
                    case GO_HALL_RUNE_3:
                        go_roomrunes[2] = go->GetGUID();
                        if (GetBossState(DATA_HALL_RUNE_3) == DONE)
                            HandleGameObject(ObjectGuid::Empty, false, go);
                        break;
                    case GO_HALL_RUNE_4:
                        go_roomrunes[3] = go->GetGUID();
                        if (GetBossState(DATA_HALL_RUNE_4) == DONE)
                            HandleGameObject(ObjectGuid::Empty, false, go);
                        break;
                    case GO_HALL_RUNE_5:
                        go_roomrunes[4] = go->GetGUID();
                        if (GetBossState(DATA_HALL_RUNE_5) == DONE)
                            HandleGameObject(ObjectGuid::Empty, false, go);
                        break;
                    case GO_HALL_RUNE_6:
                        go_roomrunes[5] = go->GetGUID();
                        if (GetBossState(DATA_HALL_RUNE_6) == DONE)
                            HandleGameObject(ObjectGuid::Empty, false, go);
                        break;
                    case GO_HALL_RUNE_7:
                        go_roomrunes[6] = go->GetGUID();
                        if (GetBossState(DATA_HALL_RUNE_7) == DONE)
                            HandleGameObject(ObjectGuid::Empty, false, go);
                        break;
                    case GO_EMBERSEER_RUNE_1:
                        go_emberseerrunes[0] = go->GetGUID();
                        if (GetBossState(DATA_PYROGAURD_EMBERSEER) == DONE)
                            HandleGameObject(ObjectGuid::Empty, false, go);
                        break;
                    case GO_EMBERSEER_RUNE_2:
                        go_emberseerrunes[1] = go->GetGUID();
                        if (GetBossState(DATA_PYROGAURD_EMBERSEER) == DONE)
                            HandleGameObject(ObjectGuid::Empty, false, go);
                        break;
                    case GO_EMBERSEER_RUNE_3:
                        go_emberseerrunes[2] = go->GetGUID();
                        if (GetBossState(DATA_PYROGAURD_EMBERSEER) == DONE)
                            HandleGameObject(ObjectGuid::Empty, false, go);
                        break;
                    case GO_EMBERSEER_RUNE_4:
                        go_emberseerrunes[3] = go->GetGUID();
                        if (GetBossState(DATA_PYROGAURD_EMBERSEER) == DONE)
                            HandleGameObject(ObjectGuid::Empty, false, go);
                        break;
                    case GO_EMBERSEER_RUNE_5:
                        go_emberseerrunes[4] = go->GetGUID();
                        if (GetBossState(DATA_PYROGAURD_EMBERSEER) == DONE)
                            HandleGameObject(ObjectGuid::Empty, false, go);
                        break;
                    case GO_EMBERSEER_RUNE_6:
                        go_emberseerrunes[5] = go->GetGUID();
                        if (GetBossState(DATA_PYROGAURD_EMBERSEER) == DONE)
                            HandleGameObject(ObjectGuid::Empty, false, go);
                        break;
                    case GO_EMBERSEER_RUNE_7:
                        go_emberseerrunes[6] = go->GetGUID();
                        if (GetBossState(DATA_PYROGAURD_EMBERSEER) == DONE)
                            HandleGameObject(ObjectGuid::Empty, false, go);
                        break;
                    case GO_PORTCULLIS_ACTIVE:
                        go_portcullis_active = go->GetGUID();
                        if (GetBossState(DATA_GYTH) == DONE)
                            HandleGameObject(ObjectGuid::Empty, true, go);
                        break;
                    case GO_PORTCULLIS_TOBOSSROOMS:
                        go_portcullis_tobossrooms = go->GetGUID();
                        if (GetBossState(DATA_GYTH) == DONE)
                            HandleGameObject(ObjectGuid::Empty, true, go);
                        break;
                    case GO_DRAKKISATH_DOOR_1:
                        go_drakkisath_door_one = go->GetGUID();
                        if (GetBossState(DATA_GENERAL_DRAKKISATH) == DONE)
                            HandleGameObject(ObjectGuid::Empty, true, go);
                        break;
                    case GO_DRAKKISATH_DOOR_2:
                        go_drakkisath_door_two = go->GetGUID();
                        if (GetBossState(DATA_GENERAL_DRAKKISATH) == DONE)
                            HandleGameObject(ObjectGuid::Empty, true, go);
                        break;
                    default:
                        break;
                }
            }
    
            bool SetBossState(uint32 type, EncounterState state) override
            {
                if (!InstanceScript::SetBossState(type, state))
                    return false;
    
                switch (type)
                {
                    case DATA_HIGHLORD_OMOKK:
                    case DATA_SHADOW_HUNTER_VOSHGAJIN:
                    case DATA_WARMASTER_VOONE:
                    case DATA_MOTHER_SMOLDERWEB:
                    case DATA_UROK_DOOMHOWL:
                    case DATA_QUARTERMASTER_ZIGRIS:
                    case DATA_GIZRUL_THE_SLAVENER:
                    case DATA_HALYCON:
                    case DATA_OVERLORD_WYRMTHALAK:
                    case DATA_PYROGAURD_EMBERSEER:
                    case DATA_WARCHIEF_REND_BLACKHAND:
                    case DATA_GYTH:
                    case DATA_THE_BEAST:
                    case DATA_GENERAL_DRAKKISATH:
                    case DATA_DRAGONSPIRE_ROOM:
                        break;
                    default:
                        break;
                }
    
                 return true;
            }
    
            void ProcessEvent(WorldObject* /*obj*/, uint32 eventId) override
            {
                switch (eventId)
                {
                    case EVENT_PYROGUARD_EMBERSEER:
                        if (GetBossState(DATA_PYROGAURD_EMBERSEER) == NOT_STARTED)
                        {
                            if (Creature* Emberseer = instance->GetCreature(PyroguardEmberseer))
                                Emberseer->AI()->SetData(1, 1);
                        }
                        break;
                    case EVENT_UROK_DOOMHOWL:
                        // handled by event_scripts
                        break;
                    default:
                        break;
                }
            }
    
            void SetData(uint32 type, uint32 data) override
            {
                switch (type)
                {
                    case AREATRIGGER:
                        if (data == AREATRIGGER_DRAGONSPIRE_HALL)
                        {
                            if (GetBossState(DATA_DRAGONSPIRE_ROOM) != DONE)
                                Events.ScheduleEvent(EVENT_DARGONSPIRE_ROOM_STORE, 1 * IN_MILLISECONDS);
                        }
                    default:
                        break;
                }
            }

            ObjectGuid GetGuidData(uint32 data) const override
            {
                switch (data)
                {
                    case DATA_HIGHLORD_OMOKK:
                        return HighlordOmokk;
                    case DATA_SHADOW_HUNTER_VOSHGAJIN:
                        return ShadowHunterVoshgajin;
                    case DATA_WARMASTER_VOONE:
                        return WarMasterVoone;
                    case DATA_MOTHER_SMOLDERWEB:
                        return MotherSmolderweb;
                    case DATA_UROK_DOOMHOWL:
                        return UrokDoomhowl;
                    case DATA_QUARTERMASTER_ZIGRIS:
                        return QuartermasterZigris;
                    case DATA_GIZRUL_THE_SLAVENER:
                        return GizrultheSlavener;
                    case DATA_HALYCON:
                        return Halycon;
                    case DATA_OVERLORD_WYRMTHALAK:
                        return OverlordWyrmthalak;
                    case DATA_PYROGAURD_EMBERSEER:
                        return PyroguardEmberseer;
                    case DATA_WARCHIEF_REND_BLACKHAND:
                        return WarchiefRendBlackhand;
                    case DATA_GYTH:
                        return Gyth;
                    case DATA_THE_BEAST:
                        return TheBeast;
                    case DATA_GENERAL_DRAKKISATH:
                        return GeneralDrakkisath;
                    case GO_EMBERSEER_IN:
                        return go_emberseerin;
                    case GO_DOORS:
                        return go_doors;
                    case GO_EMBERSEER_OUT:
                        return go_emberseerout;
                    case GO_HALL_RUNE_1:
                        return go_roomrunes[0];
                    case GO_HALL_RUNE_2:
                        return go_roomrunes[1];
                    case GO_HALL_RUNE_3:
                        return go_roomrunes[2];
                    case GO_HALL_RUNE_4:
                        return go_roomrunes[3];
                    case GO_HALL_RUNE_5:
                        return go_roomrunes[4];
                    case GO_HALL_RUNE_6:
                        return go_roomrunes[5];
                    case GO_HALL_RUNE_7:
                        return go_roomrunes[6];
                    case GO_EMBERSEER_RUNE_1:
                        return go_emberseerrunes[0];
                    case GO_EMBERSEER_RUNE_2:
                        return go_emberseerrunes[1];
                    case GO_EMBERSEER_RUNE_3:
                        return go_emberseerrunes[2];
                    case GO_EMBERSEER_RUNE_4:
                        return go_emberseerrunes[3];
                    case GO_EMBERSEER_RUNE_5:
                        return go_emberseerrunes[4];
                    case GO_EMBERSEER_RUNE_6:
                        return go_emberseerrunes[5];
                    case GO_EMBERSEER_RUNE_7:
                        return go_emberseerrunes[6];
                    case GO_PORTCULLIS_ACTIVE:
                        return go_portcullis_active;
                        break;
                    case GO_PORTCULLIS_TOBOSSROOMS:
                        return go_portcullis_tobossrooms;
                        break;
                    case GO_DRAKKISATH_DOOR_1:
                        return go_drakkisath_door_one;
                        break;
                    case GO_DRAKKISATH_DOOR_2:
                        return go_drakkisath_door_two;
                        break;
                    default:
                        break;
                }

                return ObjectGuid::Empty;
            }
    
            void Update(uint32 diff) override
            {
                Events.Update(diff);
    
                while (uint32 eventId = Events.ExecuteEvent())
                {
                    switch (eventId)
                    {
                        case EVENT_DARGONSPIRE_ROOM_STORE:
                            Dragonspireroomstore();
                            Events.ScheduleEvent(EVENT_DARGONSPIRE_ROOM_CHECK, 3 * IN_MILLISECONDS);
                            break;
                        case EVENT_DARGONSPIRE_ROOM_CHECK:
                            Dragonspireroomcheck();
                            if ((GetBossState(DATA_DRAGONSPIRE_ROOM) != DONE))
                                Events.ScheduleEvent(EVENT_DARGONSPIRE_ROOM_CHECK, 3 * IN_MILLISECONDS);
                            break;
                        default:
                             break;
                    }
                }
            }
    
            void Dragonspireroomstore()
            {
                uint8 creatureCount;
    
                for (uint8 i = 0; i < 7; ++i)
                {
                    creatureCount = 0;
    
                    if (GameObject* rune = instance->GetGameObject(go_roomrunes[i]))
                    {
                        for (uint8 j = 0; j < 3; ++j)
                        {
                            std::list<Creature*> creatureList;
                            rune->GetCreatureListWithEntryInGrid(creatureList, DragonspireMobs[j], 15.0f);
                            for (std::list<Creature*>::iterator itr = creatureList.begin(); itr != creatureList.end(); ++itr)
                            {
                                if (Creature* creature = *itr)
                                {
                                    runecreaturelist[i][creatureCount] = creature->GetGUID();
                                    ++creatureCount;
                                }
                            }
                        }
                    }
                }
            }
    
            void Dragonspireroomcheck()
            {
                Creature* mob = NULL;
                GameObject* rune = NULL;
    
                for (uint8 i = 0; i < 7; ++i)
                {
                    bool _mobAlive = false;
                    rune = instance->GetGameObject(go_roomrunes[i]);
                    if (!rune)
                        continue;
    
                    if (rune->GetGoState() == GO_STATE_ACTIVE)
                    {
                        for (uint8 ii = 0; ii < 5; ++ii)
                        {
                            mob = instance->GetCreature(runecreaturelist[i][ii]);
                            if (mob && mob->IsAlive())
                                _mobAlive = true;
                        }
                    }
    
                    if (!_mobAlive && rune->GetGoState() == GO_STATE_ACTIVE)
                    {
                        HandleGameObject(ObjectGuid::Empty, false, rune);
    
                        switch (rune->GetEntry())
                        {
                            case GO_HALL_RUNE_1:
                                SetBossState(DATA_HALL_RUNE_1, DONE);
                                break;
                            case GO_HALL_RUNE_2:
                                SetBossState(DATA_HALL_RUNE_2, DONE);
                                break;
                            case GO_HALL_RUNE_3:
                                SetBossState(DATA_HALL_RUNE_3, DONE);
                                break;
                            case GO_HALL_RUNE_4:
                                SetBossState(DATA_HALL_RUNE_4, DONE);
                                break;
                            case GO_HALL_RUNE_5:
                                SetBossState(DATA_HALL_RUNE_5, DONE);
                                break;
                            case GO_HALL_RUNE_6:
                                SetBossState(DATA_HALL_RUNE_6, DONE);
                                break;
                            case GO_HALL_RUNE_7:
                                SetBossState(DATA_HALL_RUNE_7, DONE);
                                break;
                            default:
                                break;
                        }
                    }
                }
    
                if (GetBossState(DATA_HALL_RUNE_1) == DONE && GetBossState(DATA_HALL_RUNE_2) == DONE && GetBossState(DATA_HALL_RUNE_3) == DONE &&
                    GetBossState(DATA_HALL_RUNE_4) == DONE && GetBossState(DATA_HALL_RUNE_5) == DONE && GetBossState(DATA_HALL_RUNE_6) == DONE &&
                    GetBossState(DATA_HALL_RUNE_7) == DONE)
                {
                    SetBossState(DATA_DRAGONSPIRE_ROOM, DONE);
                    if (GameObject* door1 = instance->GetGameObject(go_emberseerin))
                        HandleGameObject(ObjectGuid::Empty, true, door1);
                    if (GameObject* door2 = instance->GetGameObject(go_doors))
                        HandleGameObject(ObjectGuid::Empty, true, door2);
                }
            }
    
            protected:
                EventMap Events;
                ObjectGuid HighlordOmokk;
                ObjectGuid ShadowHunterVoshgajin;
                ObjectGuid WarMasterVoone;
                ObjectGuid MotherSmolderweb;
                ObjectGuid UrokDoomhowl;
                ObjectGuid QuartermasterZigris;
                ObjectGuid GizrultheSlavener;
                ObjectGuid Halycon;
                ObjectGuid OverlordWyrmthalak;
                ObjectGuid PyroguardEmberseer;
                ObjectGuid WarchiefRendBlackhand;
                ObjectGuid Gyth;
                ObjectGuid LordVictorNefarius;
                ObjectGuid TheBeast;
                ObjectGuid GeneralDrakkisath;
                ObjectGuid go_emberseerin;
                ObjectGuid go_doors;
                ObjectGuid go_emberseerout;
                ObjectGuid go_blackrockaltar;
                ObjectGuid go_roomrunes[7];
                ObjectGuid go_emberseerrunes[7];
                ObjectGuid runecreaturelist[7][5];
                ObjectGuid go_portcullis_active;
                ObjectGuid go_portcullis_tobossrooms;
                ObjectGuid go_drakkisath_door_one;
                ObjectGuid go_drakkisath_door_two;
        };
    
        InstanceScript* GetInstanceScript(InstanceMap* map) const override
        {
            return new instance_blackrock_spireMapScript(map);
        }
};

/*#####
# at_dragonspire_hall
#####*/

class at_dragonspire_hall : public AreaTriggerScript
{
public:
    at_dragonspire_hall() : AreaTriggerScript("at_dragonspire_hall") { }

    bool OnTrigger(Player* player, const AreaTriggerEntry* /*at*/) override
    {
        if (player && player->IsAlive())
        {
            if (InstanceScript* instance = player->GetInstanceScript())
            {
                instance->SetData(AREATRIGGER, AREATRIGGER_DRAGONSPIRE_HALL);
                return true;
            }
        }

        return false;
    }
};

/*#####
# at_blackrock_stadium
#####*/

class at_blackrock_stadium : public AreaTriggerScript
{
public:
    at_blackrock_stadium() : AreaTriggerScript("at_blackrock_stadium") { }

    bool OnTrigger(Player* player, const AreaTriggerEntry* /*at*/) override
    {
        if (player && player->IsAlive())
        {
            InstanceScript* instance = player->GetInstanceScript();
            if (!instance)
                return false;

            if (Creature* rend = player->FindNearestCreature(NPC_WARCHIEF_REND_BLACKHAND, 50.0f))
            {
                rend->AI()->SetData(AREATRIGGER, AREATRIGGER_BLACKROCK_STADIUM);
                return true;
            }
        }

        return false;
    }
};

void AddSC_instance_blackrock_spire()
{
    new instance_blackrock_spire();
    new at_dragonspire_hall();
    new at_blackrock_stadium();
}
